<?php

namespace app\common\model\banxi\core;

use app\common\enums\VocabularyLexiconStatusEnum;
use app\common\model\common\BaseModel;
use app\common\service\TTLService;
use ba\Exception;
use think\db\exception\PDOException;
use think\exception\ValidateException;
use think\model\concern\SoftDelete;

/**
 * 词汇检查词库
 */
class CoreVocabularyLexicon extends BaseModel
{
    use SoftDelete;

    protected $name = 'banxi_core_vocabulary_lexicon';

    protected $autoWriteTimestamp = true;

    protected $deleteTime = 'del';

    protected $defaultSoftDelete = 0;

    protected $append = [
        'statusCn',
        'optionList',
        'answerList'
    ];

    protected $type = [
        'id' => 'string',
        'del' => 'string',
        'settingId' => 'string',
        'isSynthesis' => 'boolean',
        'createTime' => 'timestamp:Y-m-d H:i:s',
        'updateTime' => 'timestamp:Y-m-d H:i:s',
    ];

    //-----------------------------------------------------------------------------------
    //｜                                   获取器                                         ｜
    //-----------------------------------------------------------------------------------

    public function getStatusCnAttr($value, $data)
    {
        $value = $value ?: ($data['status'] ?? '');
        return (new VocabularyLexiconStatusEnum())->getDescription($value) ?? '';
    }

    public function getOptionListAttr($value, $data)
    {
        $value = $value ?: ($data['option'] ?? '');
        return json_decode($value, true) ?? [];
    }

    public function getAnswerListAttr($value, $data)
    {
        $value = $value ?: ($data['answer'] ?? '');
        return json_decode($value, true) ?? [];
    }
    //-----------------------------------------------------------------------------------
    //｜                                   处理方法                                       ｜
    //-----------------------------------------------------------------------------------
    public function audioCompositing()
    {
        $count = $this::where(['isSynthesis' => 0])->count();
        $chunkSize = 50;
        $totalPage = ceil($count / $chunkSize);
        try {
            for ($page = 1; $page <= $totalPage; $page++) {
                $words = $this::where(['isSynthesis' => 0])
                    ->field('id,word,status')
                    ->page($page, $chunkSize)
                    ->select();
                foreach ($words as $item) {
                    try {
                        $word = $item['word'];
                        $TTLService = new TTLService();
                        $res = $TTLService->getEnVocalToUrl($word);
                        if (!$res) {
                            logSave([
                                'file' => __FILE__,
                                'message' => 'Word vocal synthesis failed',
                                'word' => $item['word'],
                                'error' => $TTLService->getError()
                            ], 'audioCompositing');
                            throw new ValidateException($TTLService->getError());
                        }
                        $count++;
                        $item->wordAudio = $res;
                        $item->isSynthesis = 1;
                        $item->save();
                    } catch (\Exception|PDOException|ValidateException $e) {
                        logSave([
                            'file' => __FILE__,
                            'message' => 'Single word vocal synthesis failed',
                            'word' => $item['word'],
                            'error' => $e->getMessage()
                        ], 'audioCompositing');
                    }
                }
            }
        } catch (\Exception $e) {
            logSave([
                'file' => __FILE__,
                'message' => 'audioCompositing error',
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ], 'audioCompositing');
        }
        return $count;
    }

    public function saveOrUpdateLexicon($lexicon)
    {
        $id = $lexicon['id'] ?? null;
        if ($id) {
            $lexiconRaw = $this->where(['id' => $id])->find();
            if (!$lexiconRaw) {
                throw new ValidateException('词汇不存在');
            }
            $word = $lexiconRaw['word'];
            if ($lexicon['word'] != $word) {
                $lexicon['isSynthesis'] = 0;
                $lexicon['wordAudio'] = '';
            }
        } else {
            $lexicon['isSynthesis'] = 0;
            $lexiconRaw = $this;
        }
        $lexicon['firstLetter'] = substr($lexicon['word'], 0, 1);
        $res = $this->getExistWord($lexicon['settingId'], $lexicon['word'], $id);
        if ($res) {
            throw new ValidateException('词汇已存在');
        }
        return $lexiconRaw->save($lexicon);
    }

    public function getExistWord(string|int $settingId, string $word, string|int|null $id = null)
    {
        $query = $this->where([
            'settingId' => $settingId,
            'word' => $word
        ]);

        if ($id) {
            $query->where('id', '<>', $id);
        }

        return $query->count() > 0;
    }

    public function importExcel($importExcelList, $settingId)
    {
        $this->startTrans();
        try {
            $words = array_map(function ($item) {
                return strtoupper($item['word']); // 转换为大写
            }, $importExcelList);

            // 对 words 数组去重，并保留原始键值
            $uniqueWords = array_unique($words);

            // 根据去重后的结果筛选原始数组
            $questionList = array_values(array_filter($importExcelList, function ($item) use ($uniqueWords) {
                return in_array(strtoupper($item['word']), $uniqueWords); // 判断是否在去重后的数组中
            }));
            $questionListCount = count($questionList);
            for ($i = 0; $i < $questionListCount; $i++) {
                $msg = '第' . ($i + 1) . '行';
                $question = $questionList[$i];
                $optionList = json_decode($question['option'], true) ?? [];
                $answerList = json_decode($question['answer'], true) ?? [];
                if (!$question['word']) {
                    throw new ValidateException($msg . '词汇不能为空');
                }
                if (!$optionList) {
                    throw new ValidateException($msg . '选项不能为空');
                }
                if (!$answerList) {
                    throw new ValidateException($msg . '答案不能为空');
                }
                if (count($answerList) > 1) {
                    throw new ValidateException($msg . '答案只能有一个');
                }
                $question['firstLetter'] = substr($question['word'], 0, 1);
                $question['status'] = VocabularyLexiconStatusEnum::E1;
                $info = $this->where([
                    'word' => $question['word'],
                    'settingId' => $settingId
                ])->find();
                if (!$info) {
                    $info = new self();
                }
                $info->save($question);
            }
            $this->commit();
        } catch (Exception|PDOException|ValidateException $e) {
            $this->rollback();
            throw new ValidateException($e->getMessage());
        }
        return true;
    }
    //-----------------------------------------------------------------------------------
    //｜                                   模型关联                                       ｜
    //-----------------------------------------------------------------------------------


}